package com.dongbao.core.rules.java;

import com.dongbao.core.base.CodeRulesCommonBase;
import com.dongbao.core.rules.CodeRulesBase;
import com.dongbao.entity.AwardedInfo;
import com.dongbao.util.PsiUtil;
import com.dongbao.util.StringArrayUtil;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.impl.source.PsiFieldImpl;
import com.intellij.psi.impl.source.PsiParameterImpl;
import com.intellij.psi.impl.source.tree.PsiCommentImpl;
import com.intellij.psi.impl.source.tree.java.PsiLocalVariableImpl;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.ui.ListUtil;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.intellij.psi.JavaDocTokenType.DOC_COMMENT_DATA;

/**
 *  Map操作请使用工具类操作,例如获取
 * */
public class MethodCommenRules extends CodeRulesCommonBase implements CodeRulesBase {

    private Map<String,PsiElement> mapVariable = new HashMap<String,PsiElement>();

    public MethodCommenRules(VirtualFile afterFile,PsiFile psiFile,Project project) {
        super(afterFile,psiFile,project);
        this.code = "B003";
        this.level = "B";
    }

    /**
     * 执行规则判断
     * */
    @Override
    public void run(List<AwardedInfo> deduction) {

        for(PsiClassImpl psiClass:psiClassImpls){
            /* 获取所有方法 */
            List<PsiMethod> ownMethods = psiClass.getOwnMethods();
            for(PsiMethod methods:ownMethods){
                /* 过滤出方法中的所有注释信息 */
                PsiElement[] elementAllCommen = PsiUtil.getElementAllCommen(methods.getBody().getContext());
                /* 获取方法体上的注释信息 */
                PsiElement firstChild = methods.getFirstChild();
                if(!(firstChild instanceof PsiComment)){
                    Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                    int lineNumber = document.getLineNumber(methods.getTextOffset());

                    packDeduction(
                            deduction,
                            psiClass.getName(),
                            psiPackageStatements.get(0).getPackageName(),
                            "",
                            String.format("方法体上未发现注释信息,方法(做两件及以上事情)的头部有注释,注释需体现整个方法的思路（最好列点说明） 只做一件事情的方法,写好方法描述."),lineNumber,lineNumber);
                }else{
                    /* 检查方法体上注释中是否存在方法体内的注释步骤 */
                    //头部注解
                    PsiComment topComment = (PsiComment)firstChild;
                    PsiElement[] commentChildren = topComment.getChildren();
                    /* 去除回车和@tag类节点 */
                    List<PsiElement> commentEffectives = Arrays.stream(commentChildren).filter(item -> {
                        return (item instanceof PsiDocToken) && ((PsiDocToken)item).getTokenType()==DOC_COMMENT_DATA;
                    }).collect(Collectors.toList());
                    /* 判断方法内的注释是否包含数字开头的流程注释 */
                    List<String> handleComments = new ArrayList<String>();
                    List<PsiElement> flowComment = Arrays.stream(elementAllCommen).filter(methodComment -> {
                        String handleComment = methodComment.getText().replace("/", "").replace("*", "").trim();
                        if(StringUtil.isNotEmpty(handleComment)&&isStartWithNumber(handleComment)){
                            handleComments.add(handleComment);
                            return true;
                        }
                        return false;
                    }).collect(Collectors.toList());
                    /* 如果存在流程注释判断是否写到了方法的头部 */
                    List<String> allCheckComment = handleComments.stream().filter(commentItem -> {
                        /* 判断是否在头部 */
                        List<PsiElement> checkComment = Arrays.stream(commentChildren).filter(topCommentItem -> {
                            if (topCommentItem.getText().contains(commentItem)) {
                                return true;
                            }
                            return false;
                        }).collect(Collectors.toList());
                        if (checkComment.size() > 0) {
                            return true;
                        }
                        return false;
                    }).collect(Collectors.toList());
                    if(allCheckComment.size()!=handleComments.size()){
                        String[] minus = StringArrayUtil.minus(allCheckComment.toArray(new String[allCheckComment.size()]), handleComments.toArray(new String[handleComments.size()]));

                        Document document = PsiDocumentManager.getInstance(psiClass.getProject()).getDocument(psiClass.getContainingFile());
                        int lineNumber = document.getLineNumber(methods.getTextOffset());
                        packDeduction(
                                deduction,
                                psiClass.getName(),
                                psiPackageStatements.get(0).getPackageName(),
                                "",
                                String.format("存在方法内流程的注释未在方法的顶部进行注释，请同步写到方法的顶部方便查看整体流程。[%s]",Arrays.asList(minus).stream().collect(Collectors.joining(",\\n")))
                                ,lineNumber,lineNumber);
                    }
                }
            }
        }
    }

    /**
     * 判断字符串是不是以数字开头
     * */
    public static boolean isStartWithNumber(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str.charAt(0)+"");
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    @Override
    public String describe() {
        return "系统公共方法和工具类";
    }
}
